
哈囉,我們又見面了,昨天我們介紹了爬蟲,爬到指定的 Youtuber 訂閱數,今天來把這功能串到 Line 的聊天機器人吧!

(這一篇還沒有要上架到 GAE,那是下一篇的事情,這篇先把 django 開在本地端就好)
在 Day23 的最後,我們創建了一個叫做 linebot 的 django 專案,裡面有 webhook 和 crawler 兩個 app,顧名思義,其中的 webhook app,就是要跟 line 做結合的 app,而 crawler app 就是用來取得資料並回傳給 webhook app 的。
這時候的專案架構應該長這樣
linebot (project dir)
├ linebot (project settings)
├ webhook (app)
├ crawler (app)
└ manage.py
這篇會直接串接昨天的爬蟲,對於第一次搭建 Line Chatbot 的捧油們會有點吃力,你可以參考 手把手教你搭建聊天機器人(LineBot+Python+QnAMaker+Heroku)-02建造LineBot Backend Server 並部署至Heroku 的 EchoBot 部分,這篇也是用 Django 做的,跟我們不同的地方是,它把 server 放到 Heroku,我們是放到 GAE,但在今天的篇幅中,不會提到部屬到雲端。
Line Developer、創建一個 Messaging API Channel在 Line Developer 詳細的申請、創建過程,可以參考 LineBot+Python,輕鬆建立聊天機器人,這一篇它是用 Flask 框架所做的,Flask 是個輕量級的 Python Web Framework,很適合拿來搭建聊天機器人這類輕量的應用,但我們已經用了 Django 一段時間,所以就試試看用 Django 來架聊天機器人囉~
你要能進到你的 Channel 設定頁面,確認三點
channel secret、webhook settings、channel access token 三個欄位Use webhook 這個欄位是有開啟的Auto-reply messages 這個欄位是關閉的確認完以上三點後,就可以進行到下個環節。
到目前為止,你已經可以在 Messaging API 分頁,透過 Bot basic ID 或 QR code,在 line 加 chatbot 好友,可是現在 chatbot 什麼功能都沒有,我們就來想辦法讓它有功能。

linebot 串到自己的 django server這句話的意思是
將使用者對 linebot 所說的話,導向你的 django server,這樣就可以在 django 端,寫下你要處理的邏輯。
在 Line Chatbot,就是透過 Webhook url 串到你的 django server 所在地,你可以把 webhook 想像成一個雙頭鉤,勾住 server 和 line 兩端,這樣就搭建起兩端溝通的橋樑了 !

Webhook url ?我一開始聽到 Webhook 的時候,就聽的霧沙沙,不能理解這是什麼鬼東東,但其實就是你 server 所在的網址,也或許是你的 server 的 IP,那麼你可能會想問,如果我的 server 架在我的電腦上,沒有固定 IP 也沒有網址,這樣該怎麼辦 ?,這時候就會想起在 Day16 所提到的 ngrok 啦 ! 它可以把你電腦的某個 port 開出來,而且最重要的是,有支援 SSL,也就是 https,因為 line 的 webhook 只支援 https。
ngrok 生成 webhook url詳細的使用方法,參考 Day16。
$ ngrok http 8000 (代表開放出 8000 的 port)
ngrok by @inconshreveable                                                (Ctrl+C to quit)
Session Status                online
Account                       rongson (Plan: Free)
Version                       2.3.35
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://xxx.ngrok.io -> http://localhost:8000
Forwarding                    https://xxx.ngrok.io -> http://localhost:8000
Connections                   ttl     opn     rt1     rt5     p50     p90
                              0       0       0.00    0.00    0.00    0.00
其中 xxx 是每次重開 ngrok 都會隨機產生,而上面這段 https://xxx.ngrok.io 就是你的 webhook url
先把 https://xxx.ngrok.io 這串複製下來,再回到瀏覽器的 Line Developer 的 Channel 頁面,Messaging API分頁 的 Webhook url 欄位,你會發現為什麼我在後面加了 /line/ 的結尾,因為我們要把 line chatbot 這功能分開,成為這個 server 的一部分,這就是開 API 的藝術的部分了,雖然我這個 API 也沒有開的多好 XD

所以現在 line 會想辦法傳訊息到 https://xxx.ngrok.io/line/, 但目前我們的 django server 還沒開好,也還沒把 line/ 的 API 開出來,所以下階段就來做 django 的部分吧。
Django Server 來跟 LineBot 對接 !Line-Bot-SDK(venv)$ pip install line-bot-sdk
補充說明,怎麼在
PyCharm IDE隨著虛擬環境所安裝的套件變化:
也就是說,PyCharm 怎麼知道我安裝了某某套件 ?
.
答案: 在 PyCharm 的File→Settings→Project: xxx→Project Interpreter→ 點齒輪 →Add...→Existing Environment→ 選擇你的虛擬環境中的 Python 執行檔

/line/ 的 API 開出來linebot/urls.py
from django.contrib import admin
from django.urls import path
from webhook.views import webhook_view
urlpatterns = [
		# 開出 line/ 的 API
		# 接著導到 `webhook` app 的 `views.py`
    path('line/', webhook_view),
    path('admin/', admin.site.urls),
]
經過設定之後,我們就開出了 http://xxx.ngrok.io/line/ 的 API,但實際裡面還沒有功能,現在來想辦法收到從 line 過來的訊息吧。
webhook/views.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from django.shortcuts import render
from django.http import JsonResponse
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponseBadRequest
from django.http import HttpResponse
from linebot.models import MessageEvent, TextMessage, TextSendMessage
from django.conf import settings
import os, time
# 呼叫 `crawler` app 的 `crawl_subscribes_of_youtuber` function
# 這部分已經在 day24 做好,參考 https://ithelp.ithome.com.tw/articles/10230271
from crawler.views import crawl_subscribes_of_youtuber
# 等等要在 `linebot/settings.py` 新增:
# ACCESS_TOKEN 和 CHANNEL_SCRET 兩個變數
line_bot_api = LineBotApi(settings.LINE_ACCESS_TOKEN)
handler = WebhookHandler(settings.CHANNEL_SECRET)
# 接收從 `linebot/urls.py` 傳過來的 request 物件
@csrf_exempt
def webhook_view(request):
		# 解析 request 的 header 是否包含 Line 的簽名
    signature = request.headers["X-Line-Signature"]
		
		# 以 UTF-8 編碼來解析 request body
    body_decode = request.body.decode('utf-8')
    try:
				# 導到 handler 來處理從 line 傳過來的訊息
        handler.handle(body_decode, signature)
    except InvalidSignatureError:
        return HttpResponseBadRequest
    return HttpResponse("OK")
# 實際處理從 line 過來的訊息的地方
@handler.add(event=MessageEvent, message=TextMessage)
def handle_message(event: MessageEvent):
		# 呼叫 `crawler/views.py` 內
		# 的 `crawl_subscribes_of_youtuber` function
    result = crawl_subscribes_of_youtuber(event.message.text)
		# 將爬蟲的結果,以 Line 純文字的訊息,回傳回去給使用者
    line_bot_api.reply_message(
        reply_token=event.reply_token,
        messages=TextSendMessage(text=result)
    )
settings.py
...
LINE_ACCESS_TOKEN = "xxxxxxxxxxx...xxxxxxx"
CHANNEL_SECRET = "yyyy...yyyy"
...
這兩個變數,可以從 Line Channel 的 Messaging API 裡的 Channel access token 找到;還有從 Line Channel 的 Basic Settings 的 Channel secret 找到。


到這邊,Django 的部分已經結束了
(venv)$ python manage.py runserver 127.0.0.1:8000
(因為我們的 ngrok 是開在 8000,所以在跑 django 的時候,還是指定 port 比較保險)

做到這邊,應該要可以通了,如果沒有,那回去確認每個環節有沒有出錯,可能出錯的環節會有
webhook 沒串起來: ngrok 沒啟動、Line Channel 的 Webhook 沒正確設定crawler app 的 code 有錯line/ API 沒有成功導向 webhook/views.py
settings.py): allow_hosts、installed_app、acesstoken、channel secret
要把實作的細節重現,還有用稍微白話一點的語句,來解釋每一個動作,真的很花時間,還以為可以在空出更多時間做其他事,沒想到我錯了 QQ
有人可能會想問我為什麼不放到 Heroku,其實我一開始就是放 Heroku,可是放上去之後,爬蟲被防火牆擋住了,可能是太多人把爬蟲放到 Heroku,直接被當成黑名單之類的,後來改放 GAE 就沒問題可以直接跑。
老話依舊,這篇文章沒有詳細到,可以讓第一次接觸的人們,直接照著我的步驟完成,其中隱藏了一些繁瑣的細節觀念,但可以給閱讀的你們實作的大方向,希望對你們有幫助,如果需要各項細節可以留言告訴我。
阿,這禮拜假日放假兩天XD
我是 RS,這是我的 不做怎麼知道系列 文章,我們 下週一見。